﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Globalization;
using System.Threading;
using Splash.Security.Cryptography;

namespace STM32_HID_BOOTLOADER
{
    public partial class mainform : Form
    {
        String szBinPath = "";
        uint[] WordData;
        byte[] FirmwareRawBytes;

        Queue<byte> RecvFifo = new Queue<byte>(4096);
        
        int Progress = 0;

        UInt32 FileCrcValue;
        UInt32 TargetCrcValue;
        int FirmwareLen;

        uint AppStartAddr = 0x8002200;

        public mainform()
        {
            InitializeComponent();
            HidBootDev.VendorId = 0x0483;
            HidBootDev.ProductId = 0x5751;
            HidBootDev.CheckDevicePresent();

            HidAppDev.VendorId = 0x0483;
            HidAppDev.ProductId = 0x5750;
            HidAppDev.CheckDevicePresent();

            Control.CheckForIllegalCrossThreadCalls = false;
        }


        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            HidBootDev.RegisterHandle(Handle);
        }

        protected override void WndProc(ref Message m)
        {

            HidBootDev.ParseMessages(ref m);
            HidAppDev.ParseMessages(ref m);
            base.WndProc(ref m);
        }

        private void HexToArry()
        {
            CRCITU crc32 = new CRCITU();
            try
            {
                
                FileStream fs;
                fs = new FileStream(szBinPath, FileMode.OpenOrCreate, FileAccess.Read);
                BinaryReader br = new BinaryReader(fs);

                FirmwareLen = (int)fs.Length;
                FirmwareRawBytes = new byte[FirmwareLen];
                FirmwareRawBytes=br.ReadBytes(FirmwareLen);
                lbl_crc32_file.Text = String.Format("文件CRC:{0:X}", stm32_crc.CRC32Software(FirmwareRawBytes, FirmwareLen));



                WordData = new uint[FirmwareLen / 4];
                for (int i = 0; i < FirmwareLen / 4; i++)
                {
                    WordData[i] = BitConverter.ToUInt32(FirmwareRawBytes, i * 4);
                }

                //WordData = new uint[fs.Length / 4];
                //for (int i = 0; i < fs.Length / 4; i++)
                //{
                //    WordData[i] = br.ReadUInt32();
                //    crc32.AppendRawData(BitConverter.GetBytes(WordData[i]));
                //}

                //lbl_crc32_file.Text=String.Format("文件CRC:{0:X}",crc32.GetCrc32());
                //FileCrcValue = crc32.GetCrc32();



                fs.Close();
                br.Close();

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void btnOpenHex_Click(object sender, EventArgs e)
        {
            try
            {
                //pbConvert.Value = 0;
                if (openHexDlg.ShowDialog() == DialogResult.OK) //打开转换的目标文件
                {
                    szBinPath = openHexDlg.FileName;
                    tbBinPath.Text = szBinPath;
                    HexToArry();
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void HidDev_OnSpecifiedDeviceRemoved(object sender, EventArgs e)
        {

        }

        private void HidDev_OnSpecifiedDeviceArrived(object sender, EventArgs e)
        {
            lbl_boot_dev_status.Text = "发现bootloader设备！";
            cbc_run.Enabled = true;
            btnProgram.Enabled = true;
        }

        private void HidDev_OnDeviceRemoved(object sender, EventArgs e)
        {
            lbl_boot_dev_status.Text = "bootloader设备不可用！";
            cbc_run.Enabled = false;
            btnProgram.Enabled = false;
        }

        private void HidDev_OnDeviceArrived(object sender, EventArgs e)
        {

        }

        private void HidDev_OnDataSend(object sender, EventArgs e)
        {

        }

        private void HidDev_OnDataRecieved(object sender, UsbLibrary.DataRecievedEventArgs args)
        {
            for (int i = 1; i < 5; i++)
            {
                RecvFifo.Enqueue(args.data[i]);
            }
        }

        private void PageErase(uint PageAddr)
        {
            byte[] Buf = new byte[65];
            Buf[0] = 0x00;
            Buf[1] = 0x02;

            byte[] AddrBytes = new byte[4];

            AddrBytes = BitConverter.GetBytes(PageAddr);
            Array.Copy(AddrBytes, 0, Buf, 5, 4);
            //Buf[5] = (byte)(((uint)PageAddr) & 0x000000ff);
            //Buf[6] = (byte)(((uint)PageAddr >> 8) & 0x000000ff);
            //Buf[7] = (byte)(((uint)PageAddr >> 16) & 0x000000ff);
            //Buf[8] = (byte)(((uint)PageAddr >> 24) & 0x000000ff);
            HidBootDev.SpecifiedDevice.SendData(Buf);
        }

        private void WordProgram(uint PageAddr, uint[] Word)
        {
            byte[] Buf = new byte[65];
            Buf[0] = 0x00;
            Buf[1] = 0x01;

            Buf[2] = (byte)Word.Length;

            //Buf[5] = (byte)(((uint)PageAddr) & 0x000000ff);
            //Buf[6] = (byte)(((uint)PageAddr >> 8) & 0x000000ff);
            //Buf[7] = (byte)(((uint)PageAddr >> 16) & 0x000000ff);
            //Buf[8] = (byte)(((uint)PageAddr >> 24) & 0x000000ff);

            byte[] AddrBytes = new byte[4];
            AddrBytes = BitConverter.GetBytes(PageAddr);
            Array.Copy(AddrBytes, 0, Buf, 5, 4);

            for (int i = 0; i < Word.Length; i++)
            {
                //Buf[9 + i * 4] = (byte)(((uint)Word[i]) & 0x000000ff);
                //Buf[10 + i * 4] = (byte)(((uint)Word[i] >> 8) & 0x000000ff);
                //Buf[11 + i * 4] = (byte)(((uint)Word[i] >> 16) & 0x000000ff);
                //Buf[12 + i * 4] = (byte)(((uint)Word[i] >> 24) & 0x000000ff);
                byte[] DataBytes = new byte[4];
                DataBytes = BitConverter.GetBytes(Word[i]);
                Array.Copy(DataBytes, 0, Buf, 9 + i * 4, 4);
            }


            HidBootDev.SpecifiedDevice.SendData(Buf);
        }

        private void Verify()
        {
            byte[] Buf = new byte[65];
            Buf[0] = 0x00;
            Buf[1] = 0x04; //Verify Cmd

            byte[] FirmwareStartAddr = new byte[4];
            FirmwareStartAddr = BitConverter.GetBytes(AppStartAddr);
            Array.Copy(FirmwareStartAddr, 0, Buf, 5,FirmwareStartAddr.Length);

            byte[] FirmwareLenArray= new byte[4];
            FirmwareLenArray = BitConverter.GetBytes(FirmwareLen);
            Array.Copy(FirmwareLenArray, 0, Buf, 9, FirmwareLenArray.Length);


            HidBootDev.SpecifiedDevice.SendData(Buf);

            while (RecvFifo.Count < 4);

            byte[] RawTargetCrcValue=new byte[4];
            RecvFifo.CopyTo(RawTargetCrcValue,0);
            RecvFifo.Clear();

            TargetCrcValue = BitConverter.ToUInt32(RawTargetCrcValue,0);

            lbl_crc32_mcu.Text = String.Format("目标CRC:{0:X}",TargetCrcValue);


        }

        private void JumpToApp()
        {
            byte[] Buf = new byte[65];
            Buf[0] = 0x00;
            Buf[1] = 0x03;



            HidBootDev.SpecifiedDevice.SendData(Buf);
        }
        
        private void ProgramTarget()
        {


            int ProgressCounter = 0;
            if (WordData != null && HidBootDev.SpecifiedDevice != null)
            {
                int PageNums = WordData.Length * 4 / 1024;
                int WordNums = WordData.Length;
                ProgressCounter = 0;
                pbar.Maximum=WordNums;
                if ((WordData.Length * 4) % 1024 != 0)
                    PageNums++;
                for (int i = 0; i < PageNums; i++)
                {
                    PageErase(AppStartAddr + (uint)i * 1024);
                }

                for (int i = 0; i < WordNums / 14; i++)
                {
                    uint[] Temp = new uint[14];
                    for (int j = 0; j < 14; j++)
                    {
                        Temp[j] = WordData[i * 14 + j];
                        ProgressCounter ++;
                        pbar.Value = ProgressCounter;
                        
                    }

                    WordProgram(AppStartAddr + (uint)i * 14 * 4, Temp);
                }
                if ((WordNums % 14) != 0)
                {
                    uint[] Temp2 = new uint[WordNums % 14];
                    for (int q = 0; q < WordNums % 14; q++)
                    {
                        Temp2[q] = WordData[WordNums - (WordNums % 14) + q];
                    }
                    WordProgram(AppStartAddr + (uint)(WordNums - (WordNums % 14)) * 4, Temp2);
                    ProgressCounter += WordNums % 14;
                }

                Verify();


                if (cbc_run.Checked)
                {
                    JumpToApp();
                }
            }
        }
        private void btnProgram_Click(object sender, EventArgs e)
        {
            Thread ProgramT = new Thread(new ThreadStart(ProgramTarget));
            ProgramT.Start();

        }

        private void btnEnterBoot_Click(object sender, EventArgs e)
        {
            if (HidAppDev.SpecifiedDevice != null)
            {
                byte[] buf = new byte[65];
                buf[0] = 0;
                buf[1] = 0x19;
                HidAppDev.SpecifiedDevice.SendData(buf);
            }
        }

        private void HidAppDev_OnSpecifiedDeviceArrived(object sender, EventArgs e)
        {
            lbl_app_dev_status.Text = "发现应用设备！";
            btnEnterBoot.Enabled = true;
        }

        private void HidAppDev_OnSpecifiedDeviceRemoved(object sender, EventArgs e)
        {
            lbl_app_dev_status.Text = "应用设备不可用！";
            btnEnterBoot.Enabled = false;
        }

        private void HidAppDev_OnDeviceRemoved(object sender, EventArgs e)
        {

        }

        private void HidAppDev_OnDeviceArrived(object sender, EventArgs e)
        {

        }

        private void HidAppDev_OnDataRecieved(object sender, UsbLibrary.DataRecievedEventArgs args)
        {

        }
    }
}
